using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using NPOI.SS.Formula.Functions;
using Pay.Core;
using Pay.Data;
using Pay.DTO;
using Pay.IService;
using Pay.Service.Cache;
using Pay.TO;

namespace Pay.Service
{
    public class ChargeService : MerchantBillService, IChargeService, IDisposable
    {
        private IUnitOfWork _iUnitOfWork;
        private IProductActivityService _iProductActivityService;
        private IProductService _iProductService;
        public ChargeService(IUnitOfWork iUnitOfWork, ISettingService iSettingService, IBillService iBillService,
                                   IMerchantService iMerchantService, IPaymentService iPaymentService,
                                   IPayHtmlService iPayHtmlService, IPayNotifyService iPayNotifyService,
                                   IProductActivityService iProductActivityService, IProductService iProductService)
                    : base(iUnitOfWork, iSettingService, iBillService, iMerchantService, iPaymentService,
                          iPayHtmlService, iPayNotifyService)
        {
            _iUnitOfWork = iUnitOfWork;
            _iProductActivityService = iProductActivityService;
            _iProductService = iProductService;
        }

        /// <summary>
        /// 付款查询
        /// </summary>
        /// <param name="chargeQuery"></param>
        /// <returns></returns>
        public ChargePage<charge_dto> Get(ChargeQuery chargeQuery)
        {
            var filter = PredicateBuilder.True<Charge>();
            if (!string.IsNullOrEmpty(chargeQuery.order_no))
                filter = filter.And(c => c.OrderNo == chargeQuery.order_no || c.PayInfo.TransactionNo == chargeQuery.order_no);
            if (chargeQuery.merchant_id > 0)
                filter = filter.And(c => c.MerchantId == chargeQuery.merchant_id);
            if (chargeQuery.type > 0)
                filter = filter.And(c => c.Type == chargeQuery.type);
            if (chargeQuery.pay_channel != PayChannel.None)
                filter = filter.And(c => c.PayInfo.PayChannel == chargeQuery.pay_channel);
            if (!string.IsNullOrEmpty(chargeQuery.merchant_no))
                filter = filter.And(c => c.Binder.MerchantNo == chargeQuery.merchant_no);
            if (chargeQuery.status != ChargeStatus.None)
                filter = filter.And(c => c.Status == chargeQuery.status);
            if (!string.IsNullOrEmpty(chargeQuery.name))
                filter = filter.And(c => c.Binder.Name.Contains(chargeQuery.name));
            if (!string.IsNullOrEmpty(chargeQuery.mobile))
                filter = filter.And(c => c.Binder.Mobile.Contains(chargeQuery.mobile));
            var start_time = chargeQuery.start_time.ToUnix();
            if (chargeQuery.start_time.HasValue && chargeQuery.end_time.HasValue)
            {
                var end_time = chargeQuery.end_time.Value.AddDays(1).ToUnix();
                filter = filter.And(c => c.OrderTime >= start_time && c.OrderTime <= end_time);
            }
            else if (chargeQuery.start_time.HasValue && !chargeQuery.end_time.HasValue)
                filter = filter.And(c => c.OrderTime >= start_time);
            else if (!chargeQuery.start_time.HasValue && chargeQuery.end_time.HasValue)
            {
                var end_time = chargeQuery.end_time.Value.AddDays(1).ToUnix();
                filter = filter.And(c => c.OrderTime <= end_time);
            }
            var result = context.Charges.FromCachePaged<Charge, charge_dto>(chargeQuery.page, chargeQuery.size, filter, orderBy => orderBy.OrderByDescending(c => c.OrderTime));
            var page = new ChargePage<charge_dto> { page = result.page, rows = result.rows, size = result.size, total_result = result.total_result };
            var amts = context.Charges.Where(filter).GroupBy(c => c.Status)
                              .Select(c => new
                              {
                                  Status = c.Key,
                                  Amount = c.Sum(o => o.Amount),
                                  PayAmount = c.Sum(o => o.PayAmount)
                              }).ToList();
            var amt = amts.Where(c => c.Status == ChargeStatus.Finish).FirstOrDefault();
            if (amt != null) { page.amount = amt.Amount; page.pay_amount = amt.PayAmount; }
            return page;
        }

        /// <summary>
        /// 查询付款
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public charge_dto Get(long id)
        {
            return context.Charges.Find(id).ToDtoObject<charge_dto>();
        }

        /// <summary>
        /// 查询付款
        /// </summary>
        /// <param name="order_no"></param>
        /// <returns></returns>
        public charge_dto Get(string merchant_no, string order_no)
        {
            return context.Charges.Where(c => c.Merchant.MerchantNo == merchant_no && c.OrderNo == order_no).FromCacheFirstOrDefault<Charge, charge_dto>("Charge" + merchant_no + order_no);
        }

        public charge_dto GetByTran(string transactionNo)
        {
            return context.Charges.FirstOrDefault(c => c.PayInfo.TransactionNo == transactionNo).ToDtoObject<charge_dto>();
        }

        private void AddByPay(charge_dto dto)
        {
            var merchant = _iMerchantService.Get(dto.merchant_no);
            dto.merchant_id = merchant.id;
            var o_payment = _iMerchantService.GetPayments(merchant.id).FirstOrDefault(c => c.payment.channel == dto.pay_info.pay_channel);
            var o_settle = o_payment.settles.First(c => c.settle_type == dto.pay_info.settle_type && c.code == dto.pay_info.pay_code);
            dto.merchant_fee = o_settle.fee;

            var plat_payment = o_payment.payment;
            var plat_settle = plat_payment.settles.First(c => c.settle_type == dto.pay_info.settle_type && c.code == dto.pay_info.pay_code);
            dto.plat_fee = plat_settle.fee;

            dto.pay_info.pay_name = o_payment.payment.name;
            dto.status = ChargeStatus.WaitPay;
            dto.order_amount = dto.pay_amount + dto.subsidy_amount;
            if (!o_payment.payment.is_open)
                throw new ServerErrorException("支付通道关闭维护，很快就开启，谢谢", 1001);
            if ((o_payment.payment.single_quota > 0 && o_payment.payment.single_quota > dto.pay_amount) ||
                (o_payment.payment.card_quota > 0 && o_payment.payment.card_quota < dto.pay_amount))
                throw new ServerErrorException("订单金额超出限额", 1001);
            if (dto.type == 3)
            {
                dto.fee = o_settle.fee;
                dto.fee_amount = (dto.pay_amount * dto.fee).RoundUp(2);
                dto.amount = dto.pay_amount - dto.fee_amount;
            }
        }

        /// <summary>
        /// 扫码支付创建对象
        /// </summary>
        /// <param name="model">Model.</param>
        public charge_dto AddByNativePay(native_model model)
        {
            var dto = model.ToDtoObject<charge_dto>();
            AddByPay(dto);
            dto.status = ChargeStatus.WaitPay;
            dto.is_out = model.settle_type == "T0";
            //SetCheckout(dto);
            return dto;
        }

        /// <summary>
        /// 拓垦用户升级创建对象
        /// </summary>
        /// <param name="model">Model.</param>
        public charge_dto AddByTokenUserUpgradePay(charge_dto dto)
        {
            var charge = context.Charges.Where(o => o.OrderNo == dto.order_no).FirstOrDefault().ToDtoObject<charge_dto>();
            if (charge != null)
                throw new ServerErrorException("业务已处理完成");

            var merchant = _iMerchantService.Get(dto.merchant_no);
            dto.merchant_id = merchant.id;

            dto.order_amount = dto.pay_amount + dto.subsidy_amount;
            dto.pay_info.pay_name = "微信支付";
            Add(dto);
            return dto;
        }

        /// <summary>
        /// 商城支付创建对象
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public charge_dto AddByMallPay(mall_model model)
        {
            //创建支付信息
            var charge = model.ToDtoObject<charge_dto>();
            charge.status = ChargeStatus.WaitPay;
            AddByPay(charge);
            bool isCoinOrder = model.orders.Any(o => o.order_type == 4);
            if (isCoinOrder)
                return charge;
            //创建订单信息
            var dt = DateTime.Now;
            //计算分账，还有验证
            merchant_dto buyer = null;
            if (!string.IsNullOrEmpty(model.bind_no)) buyer = _iMerchantService.Get(model.bind_no);
            foreach (var order in model.orders)
            {
                order.id = 0;
                decimal integral_discount = 0;
                if (buyer != null)
                {
                    order.buy_id = buyer.id;
                    charge.bind_no = buyer.merchant_no;
                    charge.bind_id = buyer.id;
                }
                order.order_time = dt;
                order.status = OrderStatus.WaitPay;
                int order_type = 0;
                foreach (var order_item in order.order_items)
                {
                    order_item.id = 0; order_item.order_id = 0;
                    var product = context.Products.Find(order_item.product_id);
                    order_type = product.Type;
                    if (order.merchant_id != 0 && product.MerchantId != order.merchant_id)
                        throw new ServerErrorException("非同家店铺不可以在同一订单中", 1001);
                    order.merchant_id = product.MerchantId;
                    if (order_item.market_price == 0) order_item.market_price = product.MarketPrice;
                    var images = product.ImagePath.ToObject<List<string>>();
                    order_item.thumbnails_url = images.Count > 0 ? images.First() : order_item.thumbnails_url;
                    if (string.IsNullOrEmpty(order_item.product_name)) order_item.product_name = product.Name;
                    order.order_amount += order_item.unit_price * order_item.quantity;
                    order.integral = order_item.integral;
                    if (order_item.integral > 0 && order_item.integral_rate > 0)
                        integral_discount += order_item.integral_rate * order_item.integral;
                }
                order.order_amount += order.freight;
                order.order_amount += order.tax;
                //if (order.integral_discount != integral_discount) {
                //    if (order.integral_discount > integral_discount + order.freight)
                //        throw new ServerErrorException("订单积分抵扣金额不正确", 1002);
                //}
                //if (order_type != 3) {
                //    //应付金额=订单金额-优惠-抵扣积分金额
                //    var p_amount = order.order_amount - order.discount_amount - order.integral_discount;
                //    if (p_amount != order.pay_amount)
                //        throw new ServerErrorException("订单支付" + order.order_no + "金额不正确", 1003);
                //}
                decimal profit = 0;
                var int_bind = order.binds.FirstOrDefault(c => c.amount < 0);
                //有负数的分账就是时代云商的积分
                if (int_bind != null)
                {
                    //要判断只有时代云商才可以执行以下业务判断
                    //卖家实收金额＝应付金额－手续费＋积分抵扣金额
#if !DEBUG
					if (model.merchant_no != "1212032877")
						throw new ServerErrorException("不符合平台的支付业务", 1006);
#endif
                    order.binds.ForEach(c => { profit += Math.Abs(c.amount); });
                    var seller_amout = (order.pay_amount - int_bind.amount);
                    if (seller_amout < profit)
                        throw new ServerErrorException("订单金额与积分抵扣金额同支付金额不同，不可以交易", 1005);
                    //获取O商为卖家的设置的卖家费率并计算手续费
                    var fee_amount = seller_amout * order.fee;
                    if (fee_amount != order.fee_amount)
                        throw new ServerErrorException("o商的手续费不正确", 1006);
                    order.amount = seller_amout - order.fee_amount;
                }
                else
                {
                    //以下电商业务判断
                    //卖家实收金额＝应付金额－手续费-分销－积分抵扣金额
                    order.binds.ForEach(c => { profit += c.amount; });
                    if (order.pay_amount < profit)
                        throw new ServerErrorException("订单金额同支付金额不同，不可以交易", 1005);
                    var fee_amount = (order.pay_amount * order.fee).RoundUp(2);
                    if (fee_amount != order.fee_amount)
                        throw new ServerErrorException("o商的手续费不正确", 1006);
                    order.amount = order.pay_amount - order.fee_amount - profit;
                }
                if (order.amount > 0 && order.binds.Count > 0)
                    charge.profit.AddRange(order.binds);
            }
            //支付金额是否正确
            var pay_amount = model.orders.Sum(c => c.pay_amount);
            if (pay_amount != charge.pay_amount)
                throw new ServerErrorException("订单支付金额不正确", 1006);
            return charge;
        }
        /// <summary>
        /// 验证订单信息
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public void ValidOrder(mall_model model)
        {
            long? buy_id = null;
            if (!string.IsNullOrEmpty(model.bind_no))
            {
                var buyer = _iMerchantService.Get(model.bind_no);
                buy_id = (buyer != null) ? buyer.id : 0;
            }
            foreach (var order in model.orders)
            {
                decimal seller_amount = 0, order_amount = 0, payAmount = 0;
                order.id = 0;
                order.buy_id = buy_id;
                order.order_time = DateTime.Now;
                order.status = OrderStatus.WaitPay;
                foreach (var order_item in order.order_items)
                {
                    order_item.id = order_item.order_id = 0;
                    //集市订单
                    if (order.order_type == 6)
                    {
                        var product = context.TokenBazaarProduct.Find(order_item.bazaar_product_id);
                        if (product == null) throw new ServerErrorException("该商品已下架");
                        if (product.Status != EBazaarProductStatus.Upper) { throw new ServerErrorException("该商品已下架"); }
                        if (product.Num <= 0) { throw new ServerErrorException("该商品已售罄"); }
                        if (product.MarketPrice != order_item.unit_price)
                        {
                            throw new ServerErrorException("该商品价格已更新");
                        }

                        order_item.thumbnails_url = product.MainImg;
                        if (string.IsNullOrEmpty(order_item.product_name))
                            order_item.product_name = product.ProductName;
                        order_amount += order_item.unit_price * order_item.quantity;
                        payAmount += product.MarketPrice * order_item.quantity;
                        seller_amount += product.MarketPrice * order_item.quantity;
                        order.merchant_id = product.MerchantId;
                        order_item.sku_path = "";
                    }
                    else if (order.order_type == 7)
                    {
                        order_item.thumbnails_url = string.Empty;
                        if (string.IsNullOrEmpty(order_item.product_name))
                            order_item.product_name = "人脸认证";
                        order_amount += order_item.unit_price * order_item.quantity;
                        decimal price = t_RequestMethod.TOKEN_FACE_Pay_Money;
                        payAmount += price * order_item.quantity;
                        seller_amount += price * order_item.quantity;
                        var merchant_no = t_RequestMethod.TOKEN_MARKETING_DEFAULT_MEARCHANTNO;
                        var sell_merchant = _iMerchantService.Get(merchant_no);
                        order.merchant_id = sell_merchant.id;
                        order_item.sku_path = "";
                    }
                    else if (order.order_type == 4)
                    {
                        var sell_merchant = _iMerchantService.Get(model.merchant_no);
                        order.merchant_id = sell_merchant.id;
                    }
                    else
                    {
                        var product = context.Products.Find(order_item.product_id);
                        var product_ex = _iProductService.GetCalculateExForActivity(order_item.product_id, order_item.sku_id ?? 0, false, order_item.isCashPay);
                        if (product_ex == null)
                            throw new ServerErrorException("该商品不存在或已下架");
                        //todo:如果是红砖兑换活动，判断当前用户红砖是否足够兑换。
                        if (order.merchant_id != 0 && order.merchant_id != product_ex.merchant_id)
                            throw new ServerErrorException("非同家店铺不可以在同一订单中", 1001);
                        if (!product_ex.is_sale)
                            throw new ServerErrorException("该商品已停止销售", 1007);
                        if (product_ex.stock <= 0)
                            throw new ServerErrorException("该商品已售罄");
                        if (product_ex.stock < order_item.quantity)
                            throw new ServerErrorException("该商品库存不足");
                        if (product_ex.price != order_item.unit_price)
                            throw new ServerErrorException("该商品价格已更新");
                        decimal cost_price = (order_item.body.Count > 0 ? Convert.ToDecimal(order_item.body["cost_price"]) : 0);
                        if (cost_price != product_ex.cost_price)
                            throw new ServerErrorException("产品价格已变更，请刷新页面");
                        if (order_item.market_price == 0)
                            order_item.market_price = product.MarketPrice;
                        var images = product.ImagePath.ToObject<List<string>>();
                        order_item.thumbnails_url = images.Count > 0 ? images.First() : order_item.thumbnails_url;
                        if (string.IsNullOrEmpty(order_item.product_name))
                            order_item.product_name = product.Name;
                        order_amount += order_item.unit_price * order_item.quantity;
                        seller_amount += cost_price * order_item.quantity;
                        order.merchant_id = product.MerchantId;
                        order_item.sku_path = "";
                    }
                }
                if (order.order_type == 4)
                {
                    order.pay_amount = order.freight;
                    order_amount += order.order_items.Sum(i => i.unit_price * i.quantity);
                    order.order_amount = order_amount + order.freight;
                    order.amount = order.pay_amount;
                    break;
                }
                order.pay_amount = order_amount - order.integral_discount + (order.freight + order.tax);
                order.order_amount = order_amount + order.freight + order.tax;
                order.amount = seller_amount + order.freight;
            }
            //支付金额是否正确
            var pay_amount = model.orders.Sum(c => c.pay_amount);
            var tran_amount = decimal.Parse(model.tran_amount) / 100;
            if (pay_amount != tran_amount)
                throw new ServerErrorException("订单支付金额不正确", 1006);
        }
        /// <summary>
        /// 快捷支付创建对象，缓存写入
        /// </summary>
        /// <param name="model">Model.</param>
        public charge_dto AddByQuickPay(quick_pay_model model)
        {
            var dto = model.ToDtoObject<charge_dto>();
            dto.is_out = model.settle_type == "T0";
            AddByPay(dto);
            //SetCheckout(dto);
            return dto;
        }

        /// <summary>
        /// 网关支付创建对象
        /// </summary>
        /// <param name="model">Model.</param>
        public int AddByGatewayPay(gateway_pay_model model)
        {
            var dto = model.ToDtoObject<charge_dto>();
            AddByPay(dto);
            dto.status = ChargeStatus.WaitPay;
            dto.is_out = false;
            Add(dto);
            int result = _iUnitOfWork.Commit();
            return result;
        }

        private Charge Add(charge_dto dto)
        {
            if (context.Charges.Exist(c => c.OrderNo == dto.order_no))
                throw new ServerErrorException("付款" + dto.order_no + "已存在", 1001);
            Charge model = dto.ToDtoObject<Charge>();
            model.OrderTime = DateTime.Now.ToUnix();
            model.PayInfo = dto.pay_info.ToDtoObject<PayInfo>();
            model.PayInfo.CreateTime = model.OrderTime;

            context.Charges.Add(model);
            return model;
        }

        private static readonly object dbLock = new object();
        /// <summary>
        /// 快捷支付成功
        /// </summary>
        /// <returns>The suceess.</returns>
        /// <param name="payHtml">临时数据</param>
        /// <param name="transaction_no">transaction no.</param>
        /// <param name="pay_time">Pay time.</param>
        public int PaySuceess(pay_html_dto payHtml, string transaction_no, DateTime? pay_time)
        {
            lock (dbLock)
            {
                var charge = payHtml.charge.ToObject<charge_dto>();
                if (charge.status != ChargeStatus.WaitPay)
                    throw new ServerErrorException("付款" + charge.order_no + "非待支付状态时", 1002);
                var db_order = context.Charges.FirstOrDefault(c => c.MerchantId == charge.merchant_id && c.PayInfo.PayNo == charge.order_no);
                if (db_order != null) return 0;

                charge.status = ChargeStatus.Finish;
                charge.pay_info.pay_time = pay_time ?? DateTime.Now;
                charge.pay_info.transaction_no = transaction_no;
                //创建收款单和账单
                var model = Add(charge);
                ChargeAccount(model);

                int result = _iUnitOfWork.Commit("Charge" + charge.merchant_no + charge.order_no);
                //if (result > 0) {
                //	if (charge.pay_info.pay_channel == PayChannel.HF && charge.pay_info.pay_type == PayType.Quick) {
                //		//创建通知消息对象
                //		_iPayNotifyService.Save(new pay_notify_dto { merchant_no = charge.merchant_no, order_no = charge.order_no, pay_channel = charge.pay_info.pay_channel, type = 1, status = (int)charge.status, create_time = DateTime.Now });
                //	}
                //}
                return result;
            }
        }

        /// <summary>
        /// 支付失败
        /// </summary>
        /// <returns>The fail.</returns>
        /// <param name="transaction_no">Transaction no.</param>
        /// <param name="fail">fail.</param>
        public int PayFail(string merchant_no, string order_no, string transaction_no, string fail = "")
        {
            var order = context.Charges.FirstOrDefault(c => c.Merchant.MerchantNo == merchant_no && c.OrderNo == order_no);
            if (order.Status == ChargeStatus.Finish)
                throw new ServerErrorException("付款" + order_no + "非支付失败或不确定状态时", 1002);

            order.Status = ChargeStatus.Fail;
            order.Fail = fail;
            if (!string.IsNullOrEmpty(transaction_no))
                order.PayInfo.TransactionNo = transaction_no;

            int result = _iUnitOfWork.Commit("Charge" + merchant_no + order_no);
            if (result > 0)
            {
                //创建通知消息对象
                _iPayNotifyService.Save(new pay_notify_dto { merchant_no = merchant_no, order_no = order_no, pay_channel = order.PayInfo.PayChannel, type = 1, status = (int)order.Status, create_time = DateTime.Now });
            }
            return result;
        }

        /// <summary>
        /// 支付失败，临时交易写入
        /// </summary>
        /// <returns>The fail.</returns>
        /// <param name="transaction_no">Transaction no.</param>
        /// <param name="fail">fail.</param>
        public int PayFail(pay_html_dto payHtml, string transaction_no, string fail = "")
        {
            var charge = payHtml.charge.ToObject<charge_dto>();
            charge.fail = fail;
            charge.pay_info.transaction_no = transaction_no;
            int result = _iPayHtmlService.Update(payHtml);
            return result;
        }

        private static readonly object dbwaitLock = new object();
        /// <summary>
        /// 支付待确认，临时交易写入
        /// </summary>
        /// <returns>The fail.</returns>
        /// <param name="payHtml">fail.</param>
        /// <param name="transaction_no">Transaction no.</param>
        public int WaitPay(pay_html_dto payHtml, string transaction_no)
        {
            lock (dbwaitLock)
            {
                var charge = payHtml.charge.ToObject<charge_dto>();
                charge.status = ChargeStatus.WaitPay;
                charge.pay_info.transaction_no = transaction_no;
                var model = Add(charge);

                int result = _iUnitOfWork.Commit("Charge" + charge.merchant_no + charge.order_no);
                return result;
            }
        }

        /// <summary>
        /// 失败或不确定支付转为成功支付
        /// </summary>
        /// <returns>The or un confirm to suceess.</returns>
        /// <param name="merchant_no">merchant no.</param>
        /// <param name="order_no">order no.</param>
        /// <param name="pay_time">Pay time.</param>
        public int FailOrUnConfirmToSuceess(string merchant_no, string order_no, DateTime? pay_time, string transaction_no = "")
        {
            var order = context.Charges.FirstOrDefault(c => c.Merchant.MerchantNo == merchant_no && c.OrderNo == order_no);
            if (order.Status == ChargeStatus.Finish)
                throw new ServerErrorException("付款" + order_no + "非支付失败或不确定状态时", 1002);

            order.Status = ChargeStatus.Finish;
            if (!order.PayInfo.PayTime.HasValue)
                order.PayInfo.PayTime = (pay_time ?? DateTime.Now).ToUnix();
            if (!string.IsNullOrEmpty(transaction_no))
                order.PayInfo.TransactionNo = transaction_no;

            //账单
            ChargeAccount(order);

            int result = _iUnitOfWork.Commit("Charge" + merchant_no + order_no);
            if (result > 0)
            {
                //创新或更新通知消息对象
                _iPayNotifyService.Save(new pay_notify_dto { merchant_no = merchant_no, order_no = order.OrderNo, pay_channel = order.PayInfo.PayChannel, type = 1, status = (int)order.Status, create_time = DateTime.Now });
            }
            return result;
        }

        /// <summary>
        /// 代收
        /// </summary>
        /// <returns>The by quick pay.</returns>
        /// <param name="model">Model.</param>
        /// <param name="status">1－到账成功，2－不确定，3－失败.</param>
        /// <param name="fail">Fail.</param>
        public int AddByQuickPay(apply_receive model, int status, string fail)
        {
            var dt = DateTime.Now;
            var charge = model.ToDtoObject<charge_dto>();
            AddByPay(charge);
            if (status == 1)
            {
                charge.status = ChargeStatus.Finish;
                charge.pay_info.pay_time = dt;
                var charge2 = Add(charge);
                ChargeAccount(charge2);
                var transfer = AddTransfer(model, TransferStatus.Finish);
                TransferAccount(transfer);
            }
            else if (status == 2)
            {
                charge.status = ChargeStatus.UnConfirm;
                Add(charge);
                AddTransfer(model, TransferStatus.None);
            }
            else
            {
                charge.status = ChargeStatus.Fail;
                charge.fail = fail;
                Add(charge);
                AddTransfer(model, TransferStatus.Fail);
            }

            int result = _iUnitOfWork.Commit();
            if (result > 0)
            {
                //创建通知消息对象
                _iPayNotifyService.Save(new pay_notify_dto { merchant_no = model.merchant_no, order_no = charge.order_no, pay_channel = charge.pay_info.pay_channel, type = 1, status = (int)charge.status, create_time = DateTime.Now });
            }
            return result;
        }

        /// <summary>
        /// 代付充值，账务走的不减余额，否则减余额
        /// </summary>
        /// <returns>The outpay.</returns>
        /// <param name="model">Model.</param>
        public int RechargeOutpay(recharge_model model)
        {
            var dt = DateTime.Now.ToUnix();
            var order_no = DateTime.Now.ToString("yyyyMMddHHmmssffff") + DtoHelper.RandCode(6);
            var bill = context.Bills.Find(model.bill_id);
            if (model.amount > bill.Deposit && !model.valid)
                throw new ServerErrorException("商户的余额不足，无法充值", 1002);
            var charge = new Charge
            {
                MerchantId = bill.MerchantId,
                BindId = bill.MerchantId,
                AccountName = model.account_name,
                Notify = "",
                AccountNo = model.account_no,
                Amount = model.amount,
                PayAmount = model.amount,
                BankName = model.bank_name,
                CardType = "DC",
                OrderAmount = model.amount,
                OrderTime = dt,
                Type = 5,
                Profit = "[]",
                Status = ChargeStatus.Finish,
                OrderNo = order_no,
                PayInfo = new PayInfo
                {
                    OrderNo = order_no,
                    CreateTime = dt,
                    PayChannel = 0,
                    PayCode = "",
                    PayType = 0,
                    SettleType = "T0",
                    TransactionNo = order_no,
                    PayName = "代付充值",
                    PayNo = order_no,
                    PayTime = dt
                }
            };
            context.Charges.Add(charge);
            if (!model.valid)
            {
                bill.Balance -= model.amount;
                bill.Deposit -= model.amount;
            }
            bill.Security += model.amount;
            int result = _iUnitOfWork.Commit();
            return result;
        }

        /// <summary>
        /// 关闭付款
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int CloseOrder(long id)
        {
            var order = context.Charges.Find(id);
            if (order.Status == ChargeStatus.Finish)
            {
                var billDetails = context.BillDetails.Where(c => c.PayId == order.PayId).ToList();
                billDetails.DataForEach(billDetail =>
                {
                    var balance = billDetail.Bill.Balance - billDetail.Amount;
                    var newDetail = new BillDetail
                    {
                        Amount = -billDetail.Amount,
                        BillId = billDetail.BillId,
                        BillType = billDetail.BillType,
                        CreateTime = billDetail.CreateTime,
                        PayId = billDetail.PayId,
                        Status = billDetail.Status,
                        Balance = balance
                    };
                    billDetail.Bill.Balance = balance;
                    context.BillDetails.Add(newDetail);
                    context.Entry(billDetail.Bill).State = System.Data.Entity.EntityState.Modified;
                });
            }
            order.Status = ChargeStatus.Close;

            int result = _iUnitOfWork.Commit("Charge" + order.Merchant.MerchantNo + order.OrderNo);
            return result;
        }

        /// <summary>
        /// 自动关闭过期没付款的付款
        /// </summary>
        /// <returns></returns>
        public List<charge_dto> CloseOrder()
        {
            var lists = new List<charge_dto>();
            try
            {
                var colse_time = 1;
                var dt = DateTime.Now.AddHours(colse_time).ToUnix();
                var charges = context.Charges.Where(c => c.OrderTime < dt && c.Status == ChargeStatus.WaitPay);
                var tags = new List<string>();
                charges.DataForEach(order =>
                {
                    order.Status = ChargeStatus.Close;
                    tags.Add("Charge" + order.Merchant.MerchantNo + order.OrderNo);
                });
                int result = _iUnitOfWork.Commit(tags.ToArray());
                if (result > 0)
                    lists = charges.ToDtoObject<List<charge_dto>>();
            }
            catch (Exception ex)
            {
                _iUnitOfWork.ErrorLog(ex);
            }
            return lists;
        }

        /// <summary>
        /// 将T0的没到账的订单转为T1，再重新代付，没有用的方法
        /// </summary>
        /// <returns>The abc.</returns>
        /// <param name="list">List.</param>
        public int ABC(List<string> list)
        {
            var charges = context.Charges.Where(c => list.Any(l => c.PayInfo.TransactionNo == l) && c.Type == 1).ToList();
            charges.DataForEach(charge =>
            {
                var settleFee = charge.SettleFee;
                var dt = DateTime.Now.ToUnix();
                charge.IsOut = false;
                charge.PayInfo.SettleType = "T1";
                charge.Amount += settleFee;
                charge.SettleFee = 0;
                context.Entry(charge.PayInfo).State = System.Data.Entity.EntityState.Modified;

                Transfer transfer = new Transfer
                {
                    AccountName = charge.RcvAccountName,
                    AccountNo = charge.RcvAccountNo,
                    ApplyTime = dt,
                    BankName = charge.RcvBankName,
                    BindId = charge.BindId.Value,
                    IdCard = charge.RcvIdCard,
                    MerchantId = charge.MerchantId,
                    Notify = "",
                    OrderNo = charge.OrderNo + "1",
                    Status = TransferStatus.Apply,
                    SettleAmount = settleFee,
                    TransferAmount = charge.Amount - settleFee,
                    Amount = charge.Amount
                };
                transfer.PayInfo = new PayInfo
                {
                    OrderNo = transfer.OrderNo,
                    PayNo = transfer.OrderNo,
                    CreateTime = dt,
                    PayChannel = charge.PayInfo.PayChannel,
                    PayType = charge.PayInfo.PayType,
                    PayCode = charge.PayInfo.PayCode,
                    PayName = charge.PayInfo.PayName,
                    SettleType = charge.PayInfo.SettleType,
                    TransactionNo = ""
                };
                context.Transfers.Add(transfer);

                var details = context.BillDetails.Where(c => c.PayId == charge.PayId && c.BillType == BillType.SettleProfit).ToList();
                details.DataForEach(c =>
                {
                    c.PayInfo = transfer.PayInfo;
                    context.Entry(c).State = System.Data.Entity.EntityState.Modified;
                });

                context.Entry(charge).State = System.Data.Entity.EntityState.Modified;
            });
            int result = _iUnitOfWork.Commit();
            return result;
        }

        /// <summary>
        /// 重整账单
        /// </summary>
        /// <returns>The acount.</returns>
        public int ReAcount()
        {
            int count = context.PayInfos.Count(); int size = 1000;
            int total_page = count / size;
            Console.WriteLine(total_page);
            if (count % size != 0) total_page = total_page + 1;
            Console.WriteLine(total_page);
            for (int page = 0; page < total_page; page++)
            {
                sb = new StringBuilder();
                StringBuilder sbstr = new StringBuilder();
                Console.WriteLine(page);
                //var payInfos = context.PayInfos.OrderByDescending(c => c.Id).ToList();
                var payInfos = context.PayInfos.OrderBy(c => c.Id).Skip(page * size).Take(size).ToList();
                int result = 0;
                foreach (var payInfo in payInfos)
                {
                    if (payInfo.PayType == PayType.Payoff)
                    {
                        //提现
                        var transfer = context.Transfers.FirstOrDefault(c => c.PayId == payInfo.Id && (c.Status == TransferStatus.Finish || c.Status == TransferStatus.Apply || c.Status == TransferStatus.Fail));
                        if (transfer != null)
                        {
                            TransferAccount(transfer);
                            Console.WriteLine("transfer:" + transfer.OrderNo);
                        }
                    }
                    else
                    {
                        //收款
                        var charge = context.Charges.FirstOrDefault(c => c.PayId == payInfo.Id && c.Status == ChargeStatus.Finish);
                        if (charge != null)
                        {
                            if (charge.Type == 1)
                            {
                                ChargeAccount(charge);
                                Console.WriteLine("charge:" + charge.OrderNo);
                            }
                            else if (charge.Type == 2)
                            {
                                if (!charge.Merchant.TradeOutPay) continue;
                                ChargeAccount(charge);
                                Console.WriteLine("charge:" + charge.OrderNo);
                            }
                            else if (charge.Type == 3)
                            {
                                var orders = context.Orders.Where(c => c.PayId == charge.PayId).ToList().ToDtoList<order_dto>();
                                MallMerchantAccount(charge, orders);
                                Console.WriteLine("order:" + charge.OrderNo);
                            }
                        }
                    }
                    result++;
                };
                sbstr.AppendLine("DECLARE @bill_id BIGINT");
                sbstr.AppendLine("DECLARE @balance decimal(18, 6)");
                sbstr.AppendLine("DECLARE @out_profit decimal(18, 6)");
                sbstr.AppendLine("DECLARE @other_amt decimal(18, 6)");
                sbstr.AppendLine("DECLARE @security decimal(18, 6)");
                sbstr.AppendLine(sb.ToString());
                File.WriteAllText(IOHelper.GetMapPath("bill-" + page + ".sql"), sbstr.ToString());
            }

            return 0;
        }
    }
}
